feat: add external account ownership verification - #806
Conversation
Add verify-ownership and verify-ownership/confirm endpoints for customer and platform external accounts, supporting wallet-signature and liveness verification methods. Adds an ownershipVerificationStatus field to ExternalAccount, EXTERNAL_ACCOUNT.OWNERSHIP_VERIFICATION_* webhook types, new error codes, and an expanded ownershipType description. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
✱ Stainless preview builds for gridThis PR will update the cli go kotlin openapi php python ruby typescript Edit this comment to update them. They will appear in their respective SDK's changelogs. ✅ grid-typescript studio · code · diff
✅ grid-openapi studio · code · diff
✅ grid-ruby studio · code · diff
✅ grid-go studio · code · diff
✅ grid-kotlin studio · code · diff
✅ grid-python studio · code · diff
✅ grid-php studio · code · diff
⏳ grid-cli studio · code · diff
⏳ These are partial results; builds are still running. This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |
…consistency Payments fire webhooks for their creation-time state (OUTGOING_PAYMENT.PENDING), so ownership verification does the same: NOT_REQUIRED and REQUIRED join the async transitions, giving webhook consumers the full lifecycle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ions resource
Replace the external-account verb endpoints (verify-ownership +
verify-ownership/confirm under /customers and /platform) with a top-level
Ownership Verifications API mirroring the KYC/KYB Verifications pattern:
- POST /ownership-verifications, GET /ownership-verifications (filter by
externalAccountId/status with cursor pagination),
GET /ownership-verifications/{verificationId}, and
POST /ownership-verifications/{verificationId}/confirm
- OwnershipVerification is a oneOf on method: WalletSignatureOwnershipVerification
(messageToSign, confirmed synchronously) and LivenessOwnershipVerification
(verificationLink + token, completes asynchronously)
- Drop ExternalAccount.ownershipVerificationStatus; add the
PENDING_OWNERSHIP_VERIFICATION external-account status for FIRST_PARTY
self-custody wallets on platforms subject to EU Travel Rule requirements
- Replace EXTERNAL_ACCOUNT.OWNERSHIP_VERIFICATION_* webhooks with
OWNERSHIP_VERIFICATION.{PENDING_REVIEW,VERIFIED,FAILED} resource webhooks
and a new EXTERNAL_ACCOUNT.STATUS_UPDATED webhook
- Trim removed error codes and extend AMOUNT_OUT_OF_RANGE description
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…less resource - Revert AMOUNT_OUT_OF_RANGE description to its original text - Rename WALLET_VERIFICATION_REQUIRED to EXTERNAL_ACCOUNT_VERIFICATION_REQUIRED - Document EXTERNAL_ACCOUNT.STATUS_UPDATED as firing on every status transition, not only ownership verification - Register the ownership_verifications resource in .stainless/stainless.yml so the endpoints reach the documented spec and SDKs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # mintlify/openapi.yaml # openapi.yaml # openapi/components/schemas/errors/Error400.yaml
Greptile SummaryThe PR adds wallet ownership-verification creation, confirmation, retrieval, listing, lifecycle schemas, and related external-account and webhook contracts.
Confidence Score: 4/5The PR appears safe to merge after correcting the non-blocking LIVENESS documentation inconsistency around the optional embed token. The API surface is coherently wired through source schemas, generated bundles, webhooks, and Stainless, but the create-endpoint description can mislead integrations into assuming every liveness provider returns an embed token. Files Needing Attention: openapi/paths/ownership_verifications/ownership-verifications.yaml
|
| Filename | Overview |
|---|---|
| openapi/paths/ownership_verifications/ownership-verifications.yaml | Defines create and list operations; the LIVENESS description incorrectly presents the provider-dependent token as guaranteed. |
| openapi/paths/ownership_verifications/ownership-verifications_{verificationId}_confirm.yaml | Defines synchronous wallet-signature confirmation and its pending, expiry, and method constraints. |
| openapi/components/schemas/ownership_verifications/OwnershipVerification.yaml | Introduces a method-discriminated union for wallet-signature and liveness verification resources. |
| openapi/components/schemas/ownership_verifications/OwnershipVerificationConfirmRequest.yaml | Defines chain-specific signature input, including the signer address and optional Bitcoin signing scheme. |
| openapi/components/schemas/ownership_verifications/LivenessOwnershipVerification.yaml | Models hosted liveness sessions and correctly makes the provider-specific embed token optional. |
| openapi/components/schemas/webhooks/OwnershipVerificationWebhook.yaml | Adds signed webhook payload contracts for pending-review, verified, and failed ownership-verification states. |
| openapi/components/schemas/external_accounts/ExternalAccountStatus.yaml | Adds the regulated PENDING_OWNERSHIP_VERIFICATION account state and documents its transfer restrictions. |
| .stainless/stainless.yml | Registers the ownership-verification resource, models, and four operations for SDK generation. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Create ownership verification] --> B{Method}
B -->|WALLET_SIGNATURE| C[Receive messageToSign]
C --> D[Wallet signs message]
D --> E[Confirm verification]
B -->|LIVENESS| F[Open verificationLink or use optional token]
F --> G[Hosted biometric flow]
E --> H{Result}
G --> H
H -->|Pending review| I[Webhook or polling]
H -->|Verified| J[External account becomes ACTIVE]
H -->|Failed| K[Start a new verification]
Prompt To Fix All With AI
### Issue 1
openapi/paths/ownership_verifications/ownership-verifications.yaml:11-12
**Liveness token documented as guaranteed**
For providers without direct SDK integration, `token` is omitted, but this endpoint says every LIVENESS response includes one, leading integrators to initialize an embedded flow without the required token instead of using `verificationLink`.
```suggestion
- `LIVENESS` — the response includes a `verificationLink` and may include a
`token` for embedding; the user completes a hosted biometric flow and
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "Make liveness verification token optiona..." | Re-trigger Greptile
Summary
Adds a first-class Ownership Verifications API for verifying that a customer (or the platform) controls a
FIRST_PARTYself-custody crypto wallet external account.Endpoints
POST /ownership-verifications— begin a verification for an external accountPOST /ownership-verifications/{verificationId}/confirm— submit the wallet signature (WALLET_SIGNATUREonly)GET /ownership-verifications/{verificationId}— fetch a verificationGET /ownership-verifications— list, filterable byexternalAccountIdandstatus, with cursor paginationResource shape
OwnershipVerificationis aoneOfdiscriminated bymethod:WALLET_SIGNATURE— returns amessageToSign; completes synchronously via the confirm endpointLIVENESS— returns a hostedverificationLinkplus an optional embedtoken(only for providers that support direct SDK integration, mirroring the KYC link contract); completes asynchronously (webhooks or polling)Per-attempt statuses:
PENDING,PENDING_REVIEW,VERIFIED,FAILED(failed attempts are retried by starting a new verification).External account changes
PENDING_OWNERSHIP_VERIFICATIONexternal-account status. It only ever appears forFIRST_PARTYself-custody wallet accounts on platforms subject to EU Travel Rule requirements, so existing integrations are unaffected. Accounts in this status can be used below regulatory transfer thresholds; completing ownership verification moves them toACTIVE.ownershipTypefield description.Webhooks
OWNERSHIP_VERIFICATION.PENDING_REVIEW,OWNERSHIP_VERIFICATION.VERIFIED, andOWNERSHIP_VERIFICATION.FAILEDevents carrying the full verification object.EXTERNAL_ACCOUNT.STATUS_UPDATEDevent carrying the full external account object, fired on any status transition (e.g.,PENDING_OWNERSHIP_VERIFICATION→ACTIVE).Error codes
WALLET_VERIFICATION_REQUIREDtoEXTERNAL_ACCOUNT_VERIFICATION_REQUIRED; removedOWNERSHIP_TYPE_REQUIRED,THIRD_PARTY_TRANSFER_LIMIT_EXCEEDED, andLIGHTNING_TRANSFER_LIMIT_EXCEEDED.Docs & SDKs
shield.svgsidebar icon for the Ownership Verifications group in the Mintlify API reference.ownership_verificationsresource block to.stainless/stainless.ymlso the endpoints flow into the documented spec and generated SDKs.Testing
make buildbundles cleanly;redocly lintandspectral lintmatch the pre-existing baseline onmainexactly (no new findings).🤖 Generated with Claude Code